//
//  NSObject+MKAPI.m
//  MKAPI
//
//  Created by 苏尚进 on 2022/7/30.
//  Copyright © 2022 moon. All rights reserved.
//

#import "NSObject+MKAPI.h"

@interface NSString (MKAPI)
@property(nonatomic,readonly,nullable) NSNumber *__mkapi_integerNumber;
@end
@implementation NSString (MKAPI)
- (NSNumber *)__mkapi_integerNumber{
    NSNumber *result = nil;
    NSInteger index = self.integerValue;
    if([self isEqualToString:[@(index) stringValue]]){
        result = @(index);
    }
    return result;
}
@end
@interface NSArray (__MKAPI)
- (nullable id)__mkapi_objectForKeyArray:(NSArray<NSString *> *)keyArray;
@end
@interface NSDictionary (__MKAPI)
- (nullable id)__mkapi_objectForKeyArray:(NSArray<NSString *> *)keyArray;
@end
@interface NSMutableArray (__MKAPI)
- (void)__mkapi_setObject:(nullable id)obj forKeyArray:(NSArray<NSString *> *)keyArray;
@end

@implementation NSArray (MKAPI)
- (nullable id)mkapi_objectForKeyPath:(NSString *)keyPath{
    return [self __mkapi_objectForKeyArray:[keyPath componentsSeparatedByString:@"."]];
}
- (nullable id)__mkapi_objectForKeyArray:(NSArray<NSString *> *)keyArray{
    id result = nil;
    if(keyArray.count==1){
        NSString *k1 = keyArray.firstObject;
        if(k1.__mkapi_integerNumber != nil){
            NSInteger index = k1.integerValue;
            if(index>=0&&index<self.count){
                result = self[index];
            }
        }
    }else if(keyArray.count>1){
        NSString *k1 = keyArray.firstObject;
        if(k1.__mkapi_integerNumber != nil){
            NSInteger index = k1.integerValue;
            if(index>=0&&index<self.count){
                id nextVal = self[index];
                if([nextVal isKindOfClass:[NSArray class]]){
                    NSArray *nextArray = nextVal;
                    result = [nextArray __mkapi_objectForKeyArray:[keyArray subarrayWithRange:NSMakeRange(1, keyArray.count-1)]];
                }else if([nextVal isKindOfClass:[NSDictionary class]]){
                    NSDictionary *nextDict = nextVal;
                    result = [nextDict __mkapi_objectForKeyArray:[keyArray subarrayWithRange:NSMakeRange(1, keyArray.count-1)]];
                }
            }
        }
    }
    return result;
}
@end

@implementation NSDictionary (MKAPI)
- (nullable id)mkapi_objectForKeyPath:(NSString *)keyPath{
    return [self __mkapi_objectForKeyArray:[keyPath componentsSeparatedByString:@"."]];
}
- (nullable id)__mkapi_objectForKeyArray:(NSArray<NSString *> *)keyArray{
    id result = nil;
    if(keyArray.count==1){
        result = [self objectForKey:keyArray.firstObject];
    }else if(keyArray.count>1){
        NSString *k1 = keyArray.firstObject;
        id val = self[k1];
        if([val isKindOfClass:[NSDictionary class]]){
            NSDictionary *nextDict = val;
            result = [nextDict __mkapi_objectForKeyArray:[keyArray subarrayWithRange:NSMakeRange(1, keyArray.count-1)]];
        }else if([val isKindOfClass:[NSArray class]]){
            NSArray *nextArray = val;
            result = [nextArray __mkapi_objectForKeyArray:[keyArray subarrayWithRange:NSMakeRange(1, keyArray.count-1)]];
        }
    }
    return result;
}
@end


@implementation NSMutableDictionary (MKAPI)
- (void)mkapi_setObject:(id)obj forKeyPath:(NSString *)keyPath{
    [self __mkapi_setObject:obj forKeyArray:[keyPath componentsSeparatedByString:@"."]];
}
- (void)__mkapi_setObject:(nullable id)obj forKeyArray:(NSArray<NSString *> *)keyArray{
    if(keyArray.count==1){
        NSString *k = keyArray.firstObject;
        if(obj){
            self[k] = obj;
        }else{
            [self removeObjectForKey:k];
        }
    }else if(keyArray.count>1){
        NSString *k1 = keyArray.lastObject;
        id preObj = [self __mkapi_objectForKeyArray:[keyArray subarrayWithRange:NSMakeRange(0, keyArray.count-1)]];
        if(!preObj){
            if(obj){
                if(k1.__mkapi_integerNumber && k1.integerValue==0){
                    NSMutableArray *preDict = [[NSMutableArray alloc] init];
                    preDict[0] = obj;
                    [self __mkapi_setObject:preDict forKeyArray:[keyArray subarrayWithRange:NSMakeRange(0, keyArray.count-1)]];
                }else{
                    NSMutableDictionary *preDict = [[NSMutableDictionary alloc] init];
                    preDict[k1] = obj;
                    [self __mkapi_setObject:preDict forKeyArray:[keyArray subarrayWithRange:NSMakeRange(0, keyArray.count-1)]];
                }
            }
        }else if([preObj isKindOfClass:[NSDictionary class]]){
            BOOL isMutable = [preObj isKindOfClass:[NSMutableDictionary class]];
            NSMutableDictionary *preDict = isMutable?preObj:[[NSMutableDictionary alloc] initWithDictionary:(NSDictionary *)preObj];
            if(obj){
                preDict[k1] = obj;
            }else{
                [preDict removeObjectForKey:k1];
            }
            if(!isMutable){
                [self __mkapi_setObject:preDict forKeyArray:[keyArray subarrayWithRange:NSMakeRange(0, keyArray.count-1)]];
            }
        }else if([preObj isKindOfClass:[NSArray class]]){
            BOOL isMutable = [preObj isKindOfClass:[NSMutableArray class]];
            if(k1.__mkapi_integerNumber != nil){
                NSInteger index = [k1 integerValue];
                NSMutableArray *preArray = isMutable?preObj:[[NSMutableArray alloc] initWithArray:(NSArray *)preObj];
                if(obj){
                    if(index>=0&&index<=preArray.count){
                        preArray[index] = obj;
                        if(!isMutable){
                            [self __mkapi_setObject:preArray forKeyArray:[keyArray subarrayWithRange:NSMakeRange(0, keyArray.count-1)]];
                        }
                    }
                }else{
                    //删除
                    if(index>=0&&index<preArray.count){
                        [preArray removeObjectAtIndex:index];
                        if(!isMutable){
                            [self __mkapi_setObject:preArray forKeyArray:[keyArray subarrayWithRange:NSMakeRange(0, keyArray.count-1)]];
                        }
                    }
                }
            }
        }else{
            if(obj){
                if(k1.__mkapi_integerNumber != nil){
                    if([k1 integerValue]==0){
                        NSMutableArray *preDict = [[NSMutableArray alloc] init];
                        preDict[0] = obj;
                        [self __mkapi_setObject:preDict forKeyArray:[keyArray subarrayWithRange:NSMakeRange(0, keyArray.count-1)]];
                    }
                }else{
                    NSMutableDictionary *preDict = [[NSMutableDictionary alloc] init];
                    preDict[k1] = obj;
                    [self __mkapi_setObject:preDict forKeyArray:[keyArray subarrayWithRange:NSMakeRange(0, keyArray.count-1)]];
                }
            }
        }
    }
}
@end

#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV
#import <MobileCoreServices/MobileCoreServices.h>
#else
#import <CoreServices/CoreServices.h>
#endif
@implementation NSString(MKAPI_UrlEndecode)
- (NSString *)mkapi_urlEncode{
    /**
     Returns a percent-escaped string following RFC 3986 for a query string key or value.
     RFC 3986 states that the following characters are "reserved" characters.
        - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
        - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="

     In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
     query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
     should be percent-escaped in the query string.
        - parameter string: The string to be percent-escaped.
        - returns: The percent-escaped string.
     */
    static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
    static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";

    NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
    [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]];

    static NSUInteger const batchSize = 50;

    NSUInteger index = 0;
    NSMutableString *escaped = @"".mutableCopy;
    NSString *string = self;
    while (index < string.length) {
        NSUInteger length = MIN(string.length - index, batchSize);
        NSRange range = NSMakeRange(index, length);

        // To avoid breaking up character sequences such as 👴🏻👮🏽
        range = [string rangeOfComposedCharacterSequencesForRange:range];

        NSString *substring = [string substringWithRange:range];
        NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
        [escaped appendString:encoded];

        index += range.length;
    }
    return escaped;
}
- (NSString *)mkapi_urlDecode{
    return [self stringByRemovingPercentEncoding];
}
- (NSString *)mkapi_contentTypeForPathExtension{
    NSString *extension = self;
    NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
    NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
    if (!contentType.length) {
        return @"application/octet-stream";
    } else {
        return contentType;
    }
}
@end

@implementation NSString (MKAPIHttpsSecurity)
- (NSArray<NSString *> *)mkapi_secPemStringArray{
    NSString *content = self;
    NSMutableArray *results = [[NSMutableArray alloc] init];
    NSInteger startIndex = 0;
    while (YES) {
        NSRange range = [content rangeOfString:@"-----BEGIN " options:NSCaseInsensitiveSearch range:NSMakeRange(startIndex, content.length-startIndex)];
        if(range.length==0) break;
        startIndex = range.location + range.length;
        range = [content rangeOfString:@"-----" options:NSCaseInsensitiveSearch range:NSMakeRange(startIndex, content.length - startIndex)];
        if(range.length==0) break;
        startIndex = range.location + range.length;
        range = [content rangeOfString:@"-----END " options:NSCaseInsensitiveSearch range:NSMakeRange(startIndex, content.length - startIndex)];
        if (range.length==0) break;
        
        NSInteger endIndex = range.location;
        NSString *rslt = [content substringWithRange:NSMakeRange(startIndex, endIndex - startIndex)];
        rslt = [rslt stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
        rslt = [rslt stringByReplacingOccurrencesOfString:@"\n" withString:@""];
        rslt = [rslt stringByReplacingOccurrencesOfString:@"\r" withString:@""];
        rslt = [rslt stringByReplacingOccurrencesOfString:@"\t" withString:@""];
        rslt = [rslt stringByReplacingOccurrencesOfString:@" " withString:@""];
        [results addObject:rslt];
        startIndex = endIndex;
    }
    
    return results;
}
- (nullable NSString *)mkapi_secPemString{
    return self.mkapi_secPemStringArray.firstObject;
}
- (nullable NSData *)mkapi_secPemData{
    NSString *pemContent = self.mkapi_secPemString;
    if(pemContent.length==0){
        return nil;
    }
    NSData *pemData = [[NSData alloc] initWithBase64EncodedString:pemContent options:NSDataBase64DecodingIgnoreUnknownCharacters];
    return pemData;
}
- (NSArray<NSData *> *)mkapi_secPemDataArray{
    NSArray<NSString *> *pemStrings = self.mkapi_secPemStringArray;
    NSMutableArray<NSData *> *results = [[NSMutableArray alloc] initWithCapacity:pemStrings.count];
    for (NSString *pemContent in pemStrings) {
        NSData *pemData = [[NSData alloc] initWithBase64EncodedString:pemContent options:NSDataBase64DecodingIgnoreUnknownCharacters];
        if(pemData){
            [results addObject:pemData];
        }
    }
    return results;
}
+ (NSString *)mkapi_stringWithSecTrustRef:(SecTrustRef)serverTrust{
    NSString *desc = nil;
    if(serverTrust!=NULL){
        NSInteger certCount = SecTrustGetCertificateCount(serverTrust);
        NSMutableArray<NSString *> *certDescs = [[NSMutableArray alloc] initWithCapacity:certCount];
        for (int i=0; i<certCount; i++) {
            SecCertificateRef certRef = SecTrustGetCertificateAtIndex(serverTrust, i);
            NSString *SubjectSummary = CFBridgingRelease(SecCertificateCopySubjectSummary(certRef));
            if(SubjectSummary){
                [certDescs addObject:SubjectSummary];
            }
        }
        desc = [certDescs componentsJoinedByString:@"|"];
    }
    return desc;
}
@end

#import <objc/runtime.h>
@implementation NSURLSessionTask(MKAPIHttpRequestAFNetworking)

static const void * const mkapi_didEvaluateServerTrustKey = &mkapi_didEvaluateServerTrustKey;
- (BOOL)mkapi_didEvaluateServerTrust {
    NSNumber *value = objc_getAssociatedObject(self, mkapi_didEvaluateServerTrustKey);
    return [value boolValue];
}
- (void)setMkapi_didEvaluateServerTrust:(BOOL)mkapi_didEvaluateServerTrust {
    objc_setAssociatedObject(self, mkapi_didEvaluateServerTrustKey, @(mkapi_didEvaluateServerTrust), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

static const void * const mkapi_authenticationChallengeKey = &mkapi_authenticationChallengeKey;
- (NSURLAuthenticationChallenge *)mkapi_authenticationChallenge{
    return objc_getAssociatedObject(self, mkapi_authenticationChallengeKey);
}
- (void)setMkapi_authenticationChallenge:(NSURLAuthenticationChallenge *)mkapi_authenticationChallenge{
    objc_setAssociatedObject(self, mkapi_authenticationChallengeKey, mkapi_authenticationChallenge, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

@implementation NSDictionary(MKAPIURL)
- (NSString *)mkapi_urlQueryStringWithUrlEncode:(BOOL)urlEncode{
    NSMutableArray *comps = [[NSMutableArray alloc] initWithCapacity:self.count];
    NSDictionary *dictionary = self;
    //对key进行排序
    NSArray<NSString *> *keys = [dictionary.allKeys sortedArrayUsingComparator:^NSComparisonResult(NSString * _Nonnull obj1, NSString * _Nonnull obj2) {
        return [obj1 compare:obj2];
    }];
    for (NSString *k in keys) {
        id v = dictionary[k];
        NSString *query = [self __mkapi_genParamForKey:k value:v urlEncode:urlEncode];
        [comps addObject:query];
    }
    NSString *result = [comps componentsJoinedByString:@"&"];
    return result;
}
- (NSString *)mkapi_keyValuesUrlEncode{
    return [self mkapi_urlQueryStringWithUrlEncode:YES];
}
- (NSString *)__mkapi_genParamForKey:(NSString *)k value:(id)v urlEncode:(BOOL)urlEncode{
    NSString *result = @"";
    if (v==[NSNull null]||!v) {
        result = k;
    }else if([v isKindOfClass:[NSArray class]]){
        NSArray *val = v;
        NSMutableArray *comps = [[NSMutableArray alloc] initWithCapacity:val.count];
        NSString *nestKey = [NSString stringWithFormat:@"%@[]",k];
        for (id nestVal in val) {
            [comps addObject:[self __mkapi_genParamForKey:nestKey value:nestVal urlEncode:urlEncode]];
        }
        result = [comps componentsJoinedByString:@"&"];
    }else if([v isKindOfClass:[NSDictionary class]]){
        NSDictionary *val = v;
        NSMutableArray *comps = [[NSMutableArray alloc] initWithCapacity:val.count];
        NSArray<NSString *> *keys = [val.allKeys sortedArrayUsingComparator:^NSComparisonResult(NSString * _Nonnull obj1, NSString * _Nonnull obj2) {
            return [obj1 compare:obj2];
        }];
        for (NSString *nestKey in keys) {
            id nestVal = val[nestKey];
            [comps addObject:[self __mkapi_genParamForKey:[NSString stringWithFormat:@"%@[%@]",k,nestKey] value:nestVal urlEncode:urlEncode]];
        }
        result = [comps componentsJoinedByString:@"&"];
    }else if([v isKindOfClass:[NSSet class]]){
        NSSet *val = v;
        NSMutableArray<NSString *> *comps = [[NSMutableArray alloc] initWithCapacity:val.count];
        for (id nestVal in val) {
            [comps addObject:[self __mkapi_genParamForKey:k value:nestVal urlEncode:urlEncode]];
        }
        [comps sortUsingComparator:^NSComparisonResult(NSString * _Nonnull obj1, NSString * _Nonnull obj2) {
            return [obj1 compare:obj2];
        }];
        result = [comps componentsJoinedByString:@"&"];
    }else{
        NSString *val = [v description];
        if(urlEncode){
            val = val.mkapi_urlEncode;
        }
        result = [NSString stringWithFormat:@"%@=%@",k,val];
    }
    return result;
}
@end

@implementation NSString(MKAPIURL)
- (NSDictionary<NSString *,id> *)mkapi_NSURLQueryItemsWithUrlDecode:(BOOL)urlDecode{
    NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
    NSString *query = self;
    NSArray *comps = [query componentsSeparatedByString:@"&"];
    NSRegularExpression *reg = [NSRegularExpression regularExpressionWithPattern:@"^(.+)\\[(.+)\\]$" options:NSRegularExpressionCaseInsensitive error:nil];//匹配：key[k]
    for (NSString *kvString in comps) {
        NSArray *kvArray = [kvString componentsSeparatedByString:@"="];
        NSString *key;
        NSString *val;
        key = kvArray.firstObject;
        if(key){
            if(kvArray.count>1){
                val = [kvString substringFromIndex:key.length+(@"=").length];
                if(urlDecode){
                    val = [val stringByRemovingPercentEncoding];
                }
            }
            if(!val){
                val = @"";
            }
            if([key hasSuffix:@"[]"]){
                NSString *nestKey = [key substringWithRange:NSMakeRange(0, key.length-(@"[]").length)];
                NSMutableArray *valueArray;
                id valueObj = result[nestKey];
                if([valueObj isKindOfClass:[NSMutableArray class]]){
                    valueArray = valueObj;
                }else if([valueObj isKindOfClass:[NSArray class]]){
                    valueArray = [[NSMutableArray alloc] initWithArray:(NSArray *)valueObj];
                }else{
                    valueArray = [[NSMutableArray alloc] init];
                }
                if(val){
                    [valueArray addObject:val];
                }
                result[nestKey] = valueArray;
            }else{
                NSArray<NSTextCheckingResult *> *regResults = [reg matchesInString:key options:NSMatchingReportCompletion range:NSMakeRange(0, key.length)];
                if(regResults.count){
                    NSTextCheckingResult *match = regResults.firstObject;
                    //正则为："^(.+)\[(.+)\]$
                    NSString *nestKey = [key substringWithRange:[match rangeAtIndex:1]];
                    NSString *innerDictKey = [key substringWithRange:[match rangeAtIndex:2]];
                    NSMutableDictionary *valueDict;
                    id valueObj = result[nestKey];
                    if([valueObj isKindOfClass:[NSMutableDictionary class]]){
                        valueDict = valueObj;
                    }else if([valueObj isKindOfClass:[NSDictionary class]]){
                        valueDict = [[NSMutableDictionary alloc] initWithDictionary:(NSMutableDictionary *)valueObj];
                    }else{
                        valueDict = [[NSMutableDictionary alloc] init];
                    }
                    if(val){
                        valueDict[innerDictKey] = val;
                    }
                    result[nestKey] = valueDict;
                }else{
                    if(val){
                        id valueObj = result[key];
                        if(valueObj){
                            NSMutableSet *valueSet;
                            if([valueObj isKindOfClass:[NSMutableSet class]]){
                                valueSet = valueObj;
                            }else if([valueObj isKindOfClass:[NSSet class]]){
                                valueSet = [[NSMutableSet alloc] initWithSet:(NSSet *)valueObj];
                            }else{
                                valueSet = [[NSMutableSet alloc] init];
                                if(![valueObj isKindOfClass:[NSArray class]]&&![valueObj isKindOfClass:[NSDictionary class]]){
                                    [valueSet addObject:valueObj];
                                }
                            }
                            [valueSet addObject:val];
                            result[key] = valueSet;
                        }else{
                            result[key] = val;
                        }
                    }
                }
            }
        }
    }
    return result;
}
- (NSDictionary<NSString *,NSString *> *)mkapi_NSURLQueryDictionaryWithUrlDecode:(BOOL)urlDecode{
    NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
    NSString *query = self;
    NSArray *comps = [query componentsSeparatedByString:@"&"];
    for (NSString *kvString in comps) {
        NSArray *kvArray = [kvString componentsSeparatedByString:@"="];
        NSString *key;
        NSString *val;
        key = kvArray.firstObject;
        if(key){
            if(kvArray.count>1){
                val = [kvString substringFromIndex:key.length+(@"=").length];
                if(urlDecode){
                    val = [val stringByRemovingPercentEncoding];
                }
            }
            if(!val){
                val = @"";
            }
            result[key] = val;
        }
    }
    return result;
}
- (NSDictionary<NSString *,NSString *> *)mkapi_NSURLQueryDictionary{
    return [self mkapi_NSURLQueryDictionaryWithUrlDecode:YES];
}
@end

#import <AssertMacros.h>
static BOOL __mkapi_serverTrustIsValid(SecTrustRef serverTrust) {
    BOOL isValid = NO;
    SecTrustResultType result;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out);
#pragma clang diagnostic pop

    isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed);

_out:
    return isValid;
}

@implementation NSURLAuthenticationChallenge(MKAPI)
- (BOOL)mkapi_verifyServerTrustWithCerts:(NSArray<NSData *> *)certs{
    NSURLAuthenticationChallenge *challenge = self;
    NSString *domain = challenge.protectionSpace.host;
    SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
    
    //验证域名
    NSMutableArray *policies = [NSMutableArray array];
    [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
    
    //验证服务器证书
    NSMutableArray *pinnedCertificates = [NSMutableArray array];
    for (NSData *certificateData in certs) {
        SecCertificateRef certRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData);
        [pinnedCertificates addObject:(__bridge_transfer id)certRef];
    }
    SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
    BOOL success = __mkapi_serverTrustIsValid(serverTrust);
    return success;
}
@end

@implementation NSData(MKAPI_Coder)

- (NSString *)mkapi_hexEncodedString{
    NSInteger len = self.length;
    NSMutableString *string = [[NSMutableString alloc] initWithCapacity:len*2];
    [self enumerateByteRangesUsingBlock:^(const void * _Nonnull bytes, NSRange byteRange, BOOL * _Nonnull stop) {
        for (NSInteger i=0; i<byteRange.length;i++) {
            Byte c = ((Byte *)bytes)[i];
            NSString *s = [NSString stringWithFormat:@"%x",c];//ascii->0-9a-zA-Z的字符串
            if(s.length==0){
                s = @"00";
            }else if(s.length==1){
                s = [NSString stringWithFormat:@"0%@",s];
            }
            [string appendString:s];
        }
    }];
    return string;
}
- (NSString *)mkapi_base64EncodedString{
    NSString *result = [self base64EncodedStringWithOptions:(NSDataBase64Encoding64CharacterLineLength)];
    return result;
}
@end
@implementation NSString(MKAPI_Desc)
+ (NSString *)mkapi_stringWithNSDataSize:(NSUInteger)size{
    NSString *desc = @"";
    if(size<1024){
        desc = [NSString stringWithFormat:@"%@ Byte",@(size)];
    }else if(size<1024*1024){
        desc = [NSString stringWithFormat:@"%.2f KB",size/1024.0];
    }else if(size<1024*1024*1024){
        desc = [NSString stringWithFormat:@"%.2f MB",size/(1024.0*1024.0)];
    }else{
        desc = [NSString stringWithFormat:@"%.2f GB",size/(1024.0*1024.0*1024.0)];
    }
    return desc;
}
@end
@implementation NSString(MKAPI_Coder)
- (char)__mkapi_hexAsciiCharAtIndex:(NSInteger)index{//返回字符串指定位置上的0-9,A-F,a-f的数字
    unichar c = [self characterAtIndex:index];
    char char_0 = 0x30;
    char char_9 = 0x39;
    char char_A = 0x41;
    char char_F = 0x46;
    char char_a = 0x61;
    char char_f = 0x66;
    char hex = 0;
    if (c>=char_0&&c<=char_9) {
        hex = c-char_0;
    }else if(c>=char_A&&c<=char_F){
        hex = 0x0A+c-char_A;
    }else if(c>=char_a&&c<=char_f){
        hex = 0x0A+c-char_a;
    }
    return hex;
}
- (NSData *)mkapi_hexDecodedData{
    NSString *hexString = self;
    const char *chars = [hexString UTF8String];
    NSInteger i = 0, len = hexString.length;

    NSMutableData *data = [NSMutableData dataWithCapacity:len / 2];
    char byteChars[3] = {'\0','\0','\0'};
    unsigned long wholeByte;

    while (i < len) {
        byteChars[0] = chars[i++];
        byteChars[1] = i<len?chars[i++]:'\0';
        wholeByte = strtoul(byteChars, NULL, 16);//strtoul()把参数 str 所指向的字符串根据给定的 base 转换为一个无符号长整数（类型为 unsigned long int 型），base 必须介于 2 和 36（包含）之间，或者是特殊值 0。
        [data appendBytes:&wholeByte length:1];
    }

    return data;
}
- (NSData *)mkapi_base64DecodedData{
    NSData *result = [[NSData alloc] initWithBase64EncodedString:self options:(NSDataBase64DecodingIgnoreUnknownCharacters)];
    return result;
}
@end

@implementation NSString(MKAPI_JSON)
- (id)mkapi_jsonObject{
    NSError *error;
    id result = [NSJSONSerialization JSONObjectWithData:[self dataUsingEncoding:NSUTF8StringEncoding] options:(NSJSONReadingOptions)0 error:&error];
    return result;
}
- (NSDictionary *)mkapi_jsonDictionary{
    NSDictionary *result = nil;
    id jsonObject = self.mkapi_jsonObject;
    if([jsonObject isKindOfClass:[NSDictionary class]]){
        result = jsonObject;
    }
    return result;
}
- (NSArray *)mkapi_jsonArray{
    NSArray *result = nil;
    id jsonObject = self.mkapi_jsonObject;
    if([jsonObject isKindOfClass:[NSArray class]]){
        result = jsonObject;
    }
    return result;
}
@end
@implementation NSDictionary(MKAPI_JSON)
- (NSString *)mkapi_jsonString{
    NSError *error;
    NSData *stringData = nil;
    if (@available(iOS 11.0, *)) {
        stringData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted|NSJSONWritingSortedKeys error:&error];
    } else {
        stringData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:&error];
    }
    NSString *result = nil;
    if(stringData){
        result = [[NSString alloc] initWithData:stringData encoding:NSUTF8StringEncoding];
    }
    return result;
}
@end

@implementation NSArray(MKAPI_JSON)
- (NSString *)mkapi_jsonString{
    NSError *error;
    NSData *stringData = nil;
    if (@available(iOS 11.0, *)) {
        stringData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted|NSJSONWritingSortedKeys error:&error];
    } else {
        stringData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:&error];
    }
    NSString *result = nil;
    if(stringData){
        result = [[NSString alloc] initWithData:stringData encoding:NSUTF8StringEncoding];
    }
    return result;
}
@end
